/**
 * 最基本的唤醒缓冲队列实现。
 */

#ifndef _AHAMOLETT_CLANG_DATASTRUCT_CFIFO_H_
#define _AHAMOLETT_CLANG_DATASTRUCT_CFIFO_H_
#ifdef __cplusplus
extern "C" {
#endif

#include <cds/cds_types.h>
#include <cds/bittool.h>

/* FIFO的结构体。 */
struct cfifo {
    size_t  content_size;   /// 当前FIFO中存放的数据大小。
    size_t  mask;           /// 通过该变量按位与保证偏移量小于缓存大小。
    int     read_ptr;       /// FIFO读指针的偏移。
    int     write_ptr;      /// FIFO写指针的偏移。
    void    *buffer;        /// 指向FIFO缓存的指针。
};


/**
 * @brief 检查fifo是否已经初始化。
 * 
 * @param fifo struct cfifo* 指向cfifo的指针。
 * 
 * @retval true 给定的cfifo已经被初始化。
 * @retval false 给定的cfifo没有被初始化。
 */
static inline int cfifo_is_initialized(struct cfifo *fifo)
{
    void *__buffer = fifo->buffer;
    size_t __mask = fifo->mask;
    return __buffer != NULL && __mask > 0;
}

/**
 * @brief 返回cfifo的缓存总大小。
 * 
 * 在给定的cfifo已经完成初始化时会返回cfifo的缓存大小；否则返回0。
 * 
 * @param fifo struct cfifo* 指向cfifo的指针。
 * 
 * @return size_t cfifo的缓存总大小。
 */
static inline size_t cfifo_size(struct cfifo *fifo)
{
    if (cfifo_is_initialized(fifo)) {
        return (fifo)->mask + 1;
    }
    return 0;
}

/**
 * @brief 检查cfifo是否可用。
 * 
 * 该函数会检查cfifo各个属性的合法性，通过该函数判断为合法的cfifo应该使用起来没任何问题。
 * 反之，如果该函数检查为不合法，则对cfifo的使用过程中可能会出现内存越界等错误。
 * 
 * @param fifo struct cfifo* 指向cfifo的指针。
 * 
 * @retval true cfifo可用。
 * @retval false cfifo不可用。
 */
static inline int cfifo_is_avail(struct cfifo *fifo)
{
    if (fifo == NULL) {
        return 0;
    }
    size_t fifo_size = cfifo_size(fifo);
    if (!is_power_of_two(fifo_size)) {
        return 0;
    }
    if (fifo->content_size > fifo_size) {
        return 0;
    }
    return 1;
}

/**
 * @brief 检查cfifo是否为空
 * 
 * @param fifo struct cfifo* 指向cfifo的指针。
 * 
 * @retval true cfifo为空。
 * @retval false cfifo不为空。
 */
#define cfifo_empty(fifo)   ((fifo)->content_size == 0)

/**
 * @brief 检查cfifo是否为满。
 * 
 * @param fifo struct cfifo* 指向cfifo的指针。
 * 
 * @retval true cfifo为满。
 * @retval false cfifo不为满。
 */
#define cfifo_full(fifo)    ((fifo)->mask < (fifo)->content_size)

/**
 * @brief 返回当前cfifo中缓存的数据大小。
 * 
 * @param fifo struct cfifo* 指向cfifo的指针。
 * 
 * @return size_t 当前cfifo中缓存的数据大小。
 */
#define cfifo_used_size(fifo)   ((fifo)->content_size)

/**
 * @brief 返回cfifo当前空闲的缓存大小。
 * 
 * @param fifo struct cfifo* 指向cfifo的指针。
 * 
 * @return size_t cfifo当前空闲的缓存大小。
 */
extern size_t cfifo_free_size(struct cfifo *fifo);

/**
 * @brief 重置cfifo。
 * 
 * 该函数会将cfifo对当前缓冲区大小，读、写指针的计数进行清零操作。
 * 
 * @param fifo struct cfifo* 指向cfifo的指针。
 */
extern void cfifo_reset(struct cfifo *fifo);

/**
 * @brief 初始化cfifo。
 * 
 * 根据给定的信息初始化一个cfifo，得到一个空的cfifo。
 * 
 * @param fifo 指向cfifo的指针。
 * @param buffer cfifo缓冲区的首地址。
 * @param buffer_size cfifo缓冲区的大小。
 * 
 * @return struct cfifo* 初始化成功返回cfifo的指针，否则返回NULL。
 */
extern struct cfifo*
cfifo_init(struct cfifo *fifo, void *buffer, size_t buffer_size);

/**
 * @brief 从cfifo的读指针开始读给定长度的数据。
 * 
 * 通过该函数可以从cfifo中读取给定长度数据，并返回实际读取的数据长度。该函数不会改变fifo的
 * 指针，仅仅对其中的数据进行读取。
 * 
 * @param fifo 指向cfifo的指针。
 * @param buffer 存放被读取数据的缓存。
 * @param size 需要读取数据的长度。
 * 
 * @return size_t 实际读取的数据长度。
 */
extern size_t cfifo_get(struct cfifo *fifo, void *buffer, size_t size);
/**
 * @brief cfifo的入队操作。
 * 
 * 将buffer中size大小的数据入队，并移动写指针。入队前会检查缓冲区的剩余大小，如果剩余的空间
 * 大于需要入队的数据，将所有数据都入队；如果剩余的空间小于需要入队的数据，则只入队剩余空间大小
 * 的数据。
 * 
 * @param fifo 指向cfifo的指针。
 * @param buffer 入队的数据。
 * @param size 入队的数据大小。
 * 
 * @return size_t 实际出队的数据大小。
 */
extern size_t cfifo_in(struct cfifo *fifo, void *buffer, size_t size);

/**
 * @brief cfifo的出队操作。
 * 
 * 将cfifo中size大小的数据出队到buffer中，并移动读指针。出队前会检查缓冲区中数据的大小，如果
 * 当前缓冲区中的数据大小大于需要出队的数据，则将给定大小的数据都出队；如果当前缓冲区中的数据
 * 大小小于需要出队的数据，则将全部数据出队。
 *
 * @param fifo 指向cfifo的指针。
 * @param buffer 出队的数据。
 * @param size 出队的数据大小。
 * 
 * @return size_t 实际出队的数据大小。
 */
extern size_t cfifo_out(struct cfifo *fifo, void *buffer, size_t size);

#ifdef __cplusplus
}
#endif
#endif  // #ifndef _AHAMOLETT_CLANG_DATASTRUCT_FIFO_H_
